#!/usr/bin/env python
import os
import sys
import getopt
import string
import itertools

"""
UBF or Universal Brute Force is a small utility designed to create a
simple yet powerful application interface for brute force writers.
It supports most of the well known application layer protocols and provide a simple interface
to extend its capabilities to support other protocol.
TODO:
    * Add default supported protocols:     HTTP, HTTPS, FTP, TFTP, TELNET, SSH, RDP, SMB, SMTP, POP, IMAP
    * Add import function that does not depend on __init__.py using imp module
"""
__version__ = '0.1'
__licence__ = ''
__copyright__ = 'Copyright 2011'
__authors__ = 'TheLeader, iosolidar'
__credits__ = ''
__maintainer__ = ''
__email__ = ''
__status__ = 'Prototype'
class ModuleNotFound(Exception): 
    """
    Raised when the user supplied module was not found in UBFPATH
    or in the current working directory or was not sepicified in the command line.
    """
class DistServer(object):
    def __init__(self, port=1337):
        self.port = port
    
class ModuleHandler(object):
    def __init__(self, mod_name, path=None):
        self.mod_name = mod_name
        module = self.search_module(mod_name, path)
        if module is None:
            raise ModuleNotFound
        else:
            self.module = module
    def search_module(self, mod_name, path=None):
        """
        Search the modules directory path:
            - If path sepcified search in path. If the module is not found in path
            return None.
            - If path is not sepcified search in environmenet variable UBFPATH
            - If path is not found in UBFPATH search in the current working directory
        """
        if path is not None:
            return self._import(mod_name, path)
        else:
            # if UBFPATH is set get its value if not set the path to the current working directory
            path = os.environ.get('UBFPATH', '.')
            return self._import(mod_name, path)
    
    def _import(self, name, path=None):
        global sys
        s = sys.path
        sys.path.insert(0, path)
        try:
            return __import__(name)
        except ImportError:
            return None
        finally:
            sys.path = s
    
    def crack(self):
        pass
class PasswordGenerator(object):
    def __init__(self, gen_type='dict', size=8, users=None, passwords=None):
        self.pwd_size = size
        self.users = users
        self.passwords = passwords
        if gen_type == 'dict':
            self.generate = self._dictionary_attack
        else:
            self.generate = self._normal_attack
    
    def _normal_attack(self):
        chars = string.letters + string.digits
        # TODO: if len(chars) > self.pwd_size: raise error
        return itertools.permutations(chars, self.pwd_size)
    def _dictionary_attack(self):
        pass
    
def print_version():
    exit('ubf version %s. %s' % (__version__, __copyright__))
def print_help():
    print """%s help:\n
  -v    --version        print version
  -h    --help            print this help message
  -u    --username        single username brute force
  -p    --password        single password brute force
  -m    --module        protocol/software handler module name
  -d    --debug            debug level information, 0 being no debug information and 5 being all information
  -D    --distributed        starts a distributed attack works together with -L
  -c    --connect        connect to an already running distributed attack used in the format host:port
  -t    --type        attack type possible values are dict or normal
  --module-search-path        points to the directory where the modules are located
  --username-file        path to a line seperated list of possible users
  --password-file        path to a line seperated list of possible passwords
""" % sys.argv[0]
def error(msg):
    sys.stderr.write("Error: %s" % msg)
if __name__ == '__main__':
    mod_name = None
    attack_vector = 'dict'
    username = None
    password = None
    # ignored if username/password are set
    u_filename = None
    p_filename = None
    output = sys.stdin
    debug_level = 1
    flag_dist = False
    dist_port = None
    mod_path = None
    try:
        opts, args = getopt.getopt(sys.argv[1:], "hvu:p:d:t:o:m:L:D",
                ['version', 'help', 
                    'username=', 'username-file=',
                    'password=', 'password-file=',
                    'debug=', 'module=',
                    'distributed', 'dist-port=',
                    'module-search-path='
                    
            ])
    except getopt.GetoptError, err:
        exit(err)
    for o, a in opts:
        if o in ('-v', '--version'):
            print_version()
        elif o in ('-h', '--help'):
            print_help()
        elif o in ('-m', '--module'):
            mod_name = a
        elif o in ('-t', '--type'):
            attack_vector = a
        elif o in ('-u', '--username'):
            username = a
        elif o in ('-p', '--password'):
            password = a
        elif o == '--username-file':
            u_filename = a
        elif o == '--password-file':
            p_filename = a
        elif o in ('-o', '--output'):
            output = a
        elif o in ('--d', '--debug'):
            debug = a
        elif o in ('-D', '--distributed'):
            flag_dist = True
        elif o in ('-L', '--dist-port'):
            dist_port = a
        elif o in ('-c', '--connect'):
            dist_host = a
        elif o == '--module-search-path':
            mod_path = a
    
    if mod_name is None:
        error("You need to supply a module\n")
        print_help()
        exit()
    
    try:
        mh = ModuleHandler(mod_name, mod_path)
        #if mh.is_valid():
        #    pg = PasswordGenerator(gen_type='normal')
        
        #    for p in pg.generate():
        #        try:
        #            mh.crack(p)
        #        except:
        #            raise
    except ModuleNotFound:
        error("Module '%s' was not found" % mod_name)
